﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using Castle.Core;
using Castle.MicroKernel.Registration;
using Castle.Windsor;
using Driven.ProcessScheduler.Interfaces;
using Driven.ProcessScheduler.WindowsService.Lib;

namespace Driven.ProcessScheduler.WindowsService
{
    public class ContainerConfigurator
    {
        public static IWindsorContainer CreateContainer()
        {
            IWindsorContainer container = new WindsorContainer();

            configureLogging(container);
            findScheduledComponents(container);
            configureScheduleManager(container);
            configureSchedulingEngine(container);

            return container;
        }

        private static List<ISchedule> createSchedules(IWindsorContainer container)
        {
            var components = new Dictionary<string, IScheduledComponent>();
            var schedules = new List<ISchedule>();

            foreach (var plugin in container.ResolveAll<IScheduledComponent>())
            {
                components.Add(plugin.GetType().FullName, plugin);
            }

            string componentSchedulesFile = Path.Combine(getPluginPath(), "ProcessSchedules.csv");

            var parser = new DelimitedTextFileParser(componentSchedulesFile, ',', true);

            foreach (var line in parser)
            {
                var componentType = line[1].Trim();

                if (!components.ContainsKey(componentType)) throw new ApplicationException(string.Format("Component '{0}' with type '{1}' specified in ProcessSchedules.csv, but not found in any assembly in folder '{2}'", line[0], line[1], getPluginPath()));

                var timeSchedule = TimeSpan.Parse(line[2].Trim());
                var repetitionSchedule = line.Length > 3 ? int.Parse(line[3].Trim()) : 0; ;
                
                ISchedule schedule = new Schedule(line[0], components[componentType], timeSchedule, repetitionSchedule);
                schedules.Add(schedule);
            }

            return schedules;
        }
        
        private static void findScheduledComponents(IWindsorContainer container)
        {
            string pluginPath = getPluginPath();
            
            foreach (var assemblyFile in Directory.GetFiles(pluginPath, "*.dll"))
            {
                var assembly = Assembly.LoadFile(assemblyFile);

                container.Register(
                    AllTypes.Of<IScheduledComponent>()
                        .FromAssembly(assembly)
                        .Configure(component => component.LifeStyle.Transient
                                                    .Named(component.Implementation.Name)
                        ));
            }
        }

        private static void configureSchedulingEngine(IWindsorContainer container)
        {
            container.Register(Component.For<SchedulingEngine>()
                                   .ImplementedBy<SchedulingEngine>());
        }

        private static void configureScheduleManager(IWindsorContainer container)
        {
            var schedules = createSchedules(container);

            container.Kernel.AddComponentInstance<IScheduleManager>(new ScheduleManager(schedules));
            
        }

        private static void configureLogging(IWindsorContainer container)
        {
            container.Register(Component.For<ILogger>()
                                   .ImplementedBy<SimpleLogger>()
                                   .LifeStyle.Is(LifestyleType.Singleton)
                                   .DependsOn(new
                                   {
                                       defaultType = SimpleLogger.SimpleLoggerType.File,
                                       applicationName = "Driven.ProcessScheduler",
                                       logFilePath = @"C:\Program Files\Driven Software\Process Scheduler\log.txt",
                                       logFileMaxSize = 2000L
                                   }
                                   ));
        }

        private static string getPluginPath()
        {
            var pluginPath = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), @".\ScheduledComponents\.");
            if (!Directory.Exists(pluginPath)) throw new ApplicationException(string.Format("Plugin path does not exist: {0}", pluginPath));
            return pluginPath;
        }

    }
}
